package org.ym.basic;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.ym.tools.FileTool;
import org.ym.tools.StringPool;
import org.ym.tools.StringTool;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

/**
 * 创建model
 * @author ym
 */
@Slf4j
public class GenerateDomainCode {
    private static final String BASE_PACKAGE = "${base.package}";
    private static final String BASE_MODEL = "${base.model}";
    private static final String BASE_MODEL_NAME = "${base.modelName}";
    private static final String BASE_TABLE = "${base.table}";
    public static void main(String[] args) {
        PackageModel packageModel = new PackageModel();
        packageModel.packageName = "org.ym.system";
        packageModel.modelName = "SysUser";
        packageModel.tableName = "sys_user";
        packageModel.variableName = "sysUser";
        generateCode(packageModel,null,null);
    }

    /**
     * 生成代码
     * @param packageModel 参数
     * @param targetClassZ 模板路径类
     */
    public static void generateCode(PackageModel packageModel,Class targetClassZ,Path workPath){
        if(targetClassZ == null){
            targetClassZ = GenerateDomainCode.class;
        }
        Path basePath = ProjectPathTool.getClassPath(targetClassZ);

        if(workPath == null) {
            workPath = basePath;
        }
        List<String> packageList = StringTool.splitToList(packageModel.getPackageName(),"\\.");
        workPath = Paths.get(workPath.toString(), packageModel.modelName);
        try {
            if(workPath.toFile().exists()) {
                FileUtils.forceDelete(workPath.toFile());
            }
            workPath = Paths.get(workPath.toString(),StringTool.join(packageList,File.separator));
            FileTool.createDirectory(workPath);
            // UI层
            createUi(basePath,workPath,packageModel);
            // application层
            createApplication(basePath,workPath,packageModel);
            // domain层
            createDomain(basePath,workPath,packageModel);
            // infrastructure层
            createInfrastructure(basePath,workPath,packageModel);
        } catch (IOException e) {
            log.error(ExceptionUtils.getExceptionTitle(packageModel),e);
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    /**
     * 创建ui层
     * @param basePath 基础路径
     * @param packageModel 定义参数
     * @param workPath 路径
     */
    private static void createUi(Path basePath, Path workPath, PackageModel packageModel) throws IOException {
        Path uiPath = Paths.get(workPath.toString(),"ui");
        FileTool.createDirectory(uiPath);
        Path templateFilePath = getTemplateFile(basePath,"Controller.txt");
        Path filePath = Paths.get(uiPath.toString(),"api",packageModel.modelName + "Controller.java");
        fillData(filePath,templateFilePath,packageModel);
        templateFilePath = getTemplateFile(basePath,"Converter.txt");
        filePath = Paths.get(uiPath.toString(),"assembler",packageModel.modelName + "Converter.java");
        fillData(filePath,templateFilePath,packageModel);
        templateFilePath = getTemplateFile(basePath,"VO.txt");
        filePath = Paths.get(uiPath.toString(),"vo",packageModel.modelName + "VO.java");
        fillData(filePath,templateFilePath,packageModel);
    }
    /**
     * 创建application层
     * @param basePath 基础路径
     * @param packageModel 定义参数
     * @param workPath 路径
     */
    private static void createApplication(Path basePath,Path workPath,PackageModel packageModel) throws IOException {
        Path applicationPath = Paths.get(workPath.toString(),"application");
        FileTool.createDirectory(applicationPath);
        Path templateFilePath = getTemplateFile(basePath,"CommandService.txt");
        Path filePath = Paths.get(applicationPath.toString(),"commandservice",packageModel.modelName + "CommandService.java");
        fillData(filePath,templateFilePath,packageModel);
        templateFilePath = getTemplateFile(basePath,"QueryService.txt");
        filePath = Paths.get(applicationPath.toString(),"queryservice",packageModel.modelName + "QueryService.java");
        fillData(filePath,templateFilePath,packageModel);
    }
    /**
     * 创建domain层
     * @param basePath 基础路径
     * @param packageModel 定义参数
     * @param workPath 路径
     */
    private static void createDomain(Path basePath,Path workPath,PackageModel packageModel) throws IOException {
        Path domainPath = Paths.get(workPath.toString(),"domain");
        FileTool.createDirectory(domainPath);
        Path templateFilePath = getTemplateFile(basePath,"Command.txt");
        Path filePath = Paths.get(domainPath.toString(),"command",packageModel.modelName + "Command.java");
        fillData(filePath,templateFilePath,packageModel);
        templateFilePath = getTemplateFile(basePath,"Model.txt");
        filePath = Paths.get(domainPath.toString(),"model","entity",packageModel.modelName + ".java");
        fillData(filePath,templateFilePath,packageModel);
        templateFilePath = getTemplateFile(basePath,"RepositoryService.txt");
        filePath = Paths.get(domainPath.toString(),"repository",packageModel.modelName + "RepositoryService.java");
        fillData(filePath,templateFilePath,packageModel);
        templateFilePath = getTemplateFile(basePath,"RepositoryServiceImpl.txt");
        filePath = Paths.get(domainPath.toString(),"repository","impl",packageModel.modelName + "RepositoryServiceImpl.java");
        fillData(filePath,templateFilePath,packageModel);

        filePath = Paths.get(domainPath.toString(),"event");
        FileTool.createDirectory(filePath);
        filePath = Paths.get(domainPath.toString(),"exceptions");
        FileTool.createDirectory(filePath);
        filePath = Paths.get(domainPath.toString(),"query");
        FileTool.createDirectory(filePath);
        filePath = Paths.get(domainPath.toString(),"model","aggregate");
        FileTool.createDirectory(filePath);
        filePath = Paths.get(domainPath.toString(),"model","objectvalue");
        FileTool.createDirectory(filePath);
        filePath = Paths.get(domainPath.toString(),"result");
        FileTool.createDirectory(filePath);
    }
    /**
     * 创建infrastructure层
     * @param basePath 基础路径
     * @param packageModel 定义参数
     * @param workPath 路径
     */
    private static void createInfrastructure(Path basePath,Path workPath,PackageModel packageModel) throws IOException {
        Path infrastructurePath = Paths.get(workPath.toString(),"infrastructure");
        FileTool.createDirectory(infrastructurePath);
        Path templateFilePath = getTemplateFile(basePath,"Repository.txt");
        Path filePath = Paths.get(infrastructurePath.toString(),"persistence",packageModel.modelName + "Repository.java");
        fillData(filePath,templateFilePath,packageModel);
    }

    /**
     * 填充数据
     * @param filePath 输出文件
     * @param templateFilePath 模板文件
     * @param packageModel 定义的参数
     * @throws IOException 错误
     */
    private static void fillData(Path filePath,Path templateFilePath,PackageModel packageModel) throws IOException {
        FileTool.createFile(filePath);
        try(OutputStreamWriter out = new OutputStreamWriter(Files.newOutputStream(filePath.toFile().toPath()), StandardCharsets.UTF_8);
            InputStreamReader in = new InputStreamReader(Files.newInputStream(templateFilePath.toFile().toPath()),StandardCharsets.UTF_8);
            BufferedReader fileReader = new BufferedReader(in);
            BufferedWriter fileWriter = new BufferedWriter(out)
        ){
            String str = fileReader.readLine();
            while (str != null){
                String newStr = str.replace(BASE_PACKAGE,packageModel.packageName)
                        .replace(BASE_MODEL,packageModel.modelName)
                        .replace(BASE_MODEL_NAME,packageModel.variableName)
                        .replace(BASE_TABLE,packageModel.tableName)
                        ;
                fileWriter.write(newStr);
                fileWriter.newLine();
                fileWriter.flush();
                str = fileReader.readLine();
            }
        }
    }

    /**
     * 获取模板文件
     * @param workPath 路径
     * @param fileName 模板文件名称
     * @return 路径
     */
    private static Path getTemplateFile(Path workPath, String fileName){
        return Paths.get(workPath.toString(), StringPool.CLASSES,"generateCode",fileName);
    }
    @Data
    public static class PackageModel{
        /**
         * 包名
         */
        private String packageName;
        /**
         * 类名
         */
        private String modelName;
        /**
         * 表名
         */
        private String tableName;
        /**
         * 变量名
         */
        private String variableName;
    }
}
